﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef SHARED_DBC_DBCLOADER_H_
#define SHARED_DBC_DBCLOADER_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <NodeCpp/Macros.h>
#include <NodeCpp/ByteConverter.h>

enum FieldFormat
{
    DBC_FF_NA = 'x',              // ignore/ default, 4 byte size, in Source String means field is ignored, in Dest String means field is filled with default value
    DBC_FF_NA_BYTE = 'X',         // ignore/ default, 1 byte size, see above
    DBC_FF_NA_FLOAT = 'F',        // ignore/ default,  float size, see above
    DBC_FF_NA_POINTER = 'p',      // fill default value into dest, pointer size, Use this only with static _data (otherwise mem-leak)
    DBC_FF_STRING = 's',          // char*
    DBC_FF_FLOAT = 'f',           // float
    DBC_FF_INT = 'i',             // std::uint32_t
    DBC_FF_BYTE = 'b',            // std::uint8_t
    DBC_FF_SORT = 'd',            // sorted by this field, field is not included
    DBC_FF_IND = 'n',             // the same,but parsed to _data
    DBC_FF_LOGIC = 'l'            // Logical (boolean)
};

/* DBC加载器 */
class DBCLoader
{
public:
    DBCLoader(void);
    virtual ~DBCLoader(void);

    virtual bool Load(const char* _Filename, const char* _Fmt) = 0;

    class Record
    {
    public:
        float GetFloat(std::size_t _Field) const
        {
            NODECPP_ASSERT(_Field < File_.FieldCount_);
            float _Val = *reinterpret_cast<float*>(Offset_ + File_.GetOffset(_Field));
            NodeCpp::ByteConverter::EndianConvert(_Val);
            return _Val;
        }
        std::uint32_t GetUInt(std::size_t _Field) const
        {
            NODECPP_ASSERT(_Field < File_.FieldCount_);
            std::uint32_t _Val = *reinterpret_cast<std::uint32_t*>(Offset_ + File_.GetOffset(_Field));
            NodeCpp::ByteConverter::EndianConvert(_Val);
            return _Val;
        }
        std::uint8_t GetUInt8(std::size_t _Field) const
        {
            NODECPP_ASSERT(_Field < File_.FieldCount_);
            return *reinterpret_cast<std::uint8_t*>(Offset_ + File_.GetOffset(_Field));
        }
        const char* GetString(std::size_t _Field) const
        {
            NODECPP_ASSERT(_Field < File_.FieldCount_);
            std::size_t _StringOffset = GetUInt(_Field);
            NODECPP_ASSERT(_StringOffset < File_.StringSize_);
            return reinterpret_cast<char*>(File_.StringTable_ + _StringOffset);
        }

    private:
        Record(DBCLoader& _File, unsigned char* _Offset) : Offset_(_Offset), File_(_File) {}

        unsigned char* Offset_;
        DBCLoader& File_;
        friend class DBCLoader;
    };

    Record GetRecord(std::size_t _ID);
    std::uint32_t GetNumRows() const { return RecordCount_; }
    std::uint32_t GetCols() const { return FieldCount_; }
    std::uint32_t GetOffset(std::size_t _ID) const { return (FieldsOffset_ != NULL && _ID < FieldCount_) ? FieldsOffset_[_ID] : 0; }
    bool IsLoaded(void) { return (Data_ != NULL); }
    char* AutoProduceData(const char* _Fmt, std::uint32_t& _Count, char**& _IndexTable);
    char* AutoProduceStrings(const char* _Fmt, char* _DataTable);
    static std::uint32_t GetFormatRecordSize(const char* _Fmt, std::int32_t* _IndexPos = NULL);

protected:
    std::uint32_t RecordSize_;
    std::uint32_t RecordCount_;
    std::uint32_t FieldCount_;
    std::uint32_t StringSize_;
    std::uint32_t* FieldsOffset_;
    unsigned char* Data_;
    unsigned char* StringTable_;
};

#endif /* SHARED_DBC_DBCLOADER_H_ */
